package com.gitee.wsl.struct.grammar.ast.node

import com.gitee.wsl.struct.grammar.ast.visitor.Visitor
import kotlin.jvm.JvmName

/**
 * Returns a tree that combines [this] and [other] tree on the same node-level.
 */
@JvmName("plus")
operator fun ASTNode.plus(other: ASTNode): List<ASTNode> {
    return listOf(this, other)
}

@JvmName("plusMany")
operator fun ASTNode.plus(others: List<ASTNode>): List<ASTNode> {
    return listOf(this) + others
}

@JvmName("plusNullable")
operator fun ASTNode.plus(other: ASTNode?): List<ASTNode> {
    return if (other == null) listOf(this) else this + other
}

@JvmName("nullablePlus")
operator fun ASTNode?.plus(other: ASTNode): List<ASTNode> {
    return if (this == null) listOf(other) else this + other
}

@JvmName("nullablePlusNullable")
operator fun ASTNode?.plus(other: ASTNode?): List<ASTNode> {
    return when {
        this == null -> listOfNotNull(other)
        other == null -> listOf(this)
        else -> this + other
    }
}

fun ASTNode.accept(visitor: Visitor) {
    visitor.visitNode(this)
}

fun ASTNode.acceptChildren(visitor: Visitor) {
    for (child in children) {
        child.accept(visitor)
    }
}

fun ASTNode.findChildOfType(type: NodeType): ASTNode? {
    return children.firstOrNull { it.type == type }
}

fun ASTNode.getParentOfType(vararg types: NodeType): ASTNode? {
    var currentNode = parent
    while (currentNode != null && !types.contains(currentNode.type)) {
        currentNode = currentNode.parent
    }
    return currentNode
}

fun ASTNode.getTextInNode(allFileText: CharSequence): CharSequence {
    return allFileText.subSequence(startOffset, endOffset)
}

/**
 * Find a child node recursive
 */
fun ASTNode.findChildOfTypeRecursive(type: NodeType): ASTNode? {
    children.forEach {
        if (it.type == type) {
            return it
        } else {
            val found = it.findChildOfTypeRecursive(type)
            if (found != null) {
                return found
            }
        }
    }
    return null
}

/**
 * Helper function to drop the first and last element in the children list.
 * E.g. we don't want to render the brackets of a link
 */
fun List<ASTNode>.innerList(): List<ASTNode> = this.subList(1, this.size - 1)

/**
 * Helper function to filter out items within a list of nodes, not of interest for the bullet list.
 */
/*fun List<ASTNode>.filterNonListTypes(): List<ASTNode> = this.filter { n ->
    n.type != ElementType.ORDERED_LIST && n.type != ElementType.UNORDERED_LIST && n.type != MarkdownTokenTypes.EOL
}*/


/**
 * @return the next sibling node. Notice that children of a sibling collection are considered siblings
 * and not the collection itself.
 */
val ASTNode.nextSibling: ASTNode?
    get() {
        if (this.parent != null) {
            val siblings = this.parent!!.children
            val index = siblings.indexOf(this)
            return if (index == siblings.size - 1) null else siblings[index + 1]
        }
        return null
    }

/**
 * @return the previous sibling. Notice that children of a sibling collection are considered siblings
 * and not the collection itself.
 */
val ASTNode.previousSibling: ASTNode?
    get() {
        if (this.parent != null) {
            val siblings = this.parent!!.children
            val index = siblings.indexOf(this)
            return if (index == 0) null else siblings[index - 1]
        }
        return null
    }
